//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2011 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_ITERATOR_INDIRECT_H_
#define _BUOLA_ITERATOR_INDIRECT_H_

#include <buola/iterator/transform.h>
#include <buola/iterator/range.h>
#include <buola/functors/transform/unary.h>

namespace buola {

///\addtogroup iterators
///@{

///\name Indirect
//@{

///returns an \ref ITransform adapted iterator which dereferences pointers

///\param pSub the underlying iterator
///
///When the iterator is dereferenced, it will return the object pointed to by `*pSub`, the result of dereferencing
///the underlying iterator
///
///Any other operation on this iterator is as if it was performed on the underlying iterator
template<typename tSub>
inline ITransform<tSub,fn::ADereference> indirect_iterator(const tSub &pSub)
{
    return {pSub,fn::ADereference()};
}

///returns an indirect iterator pointing to the beginning of the range \p pRange

///\param pRange the range to adapt
///
///This is useful to traverse a range of pointers as if it was a range of the pointed-to objects
///
///It is equivalent to `indirect_iterator(range_begin(pRange))`
///
///For example,
///
///~~~~~~~~~~~~~~~~~
///void print_numbers(const std::vector<int*> &pVector)
///{
///    std::copy(indirect_begin(pVector),indirect_end(pVector),io::ITextWriter<int>(gOut," "));
///}
///~~~~~~~~~~~~~~~~~
///
///would print the values pointed-to by the elements in pVector.
template<typename tRange>
inline ITransform<ORangeIterator<tRange>,fn::ADereference> indirect_begin(tRange &&pRange)
{
    return {range_begin(pRange),fn::ADereference()};
}

///returns an indirect iterator pointing past the end of the range \p pRange

///\param pRange the range to adapt
///
///This is useful to traverse a range of pointers as if it was a range of the pointed-to objects
///
///It is equivalent to `indirect_iterator(range_end(pRange))`
template<typename tRange>
inline ITransform<ORangeIterator<tRange>,fn::ADereference> indirect_end(tRange &&pRange)
{
    return {range_end(pRange),fn::ADereference()};
}

///returns a range that can be used to access the objects pointed-to by another range

///\param pB the beginning of the range of pointers
///\param pE the end of the range of pointers
///
///The range will contain the elements pointed to by the elements in the range [\p pB,\p pE)
///
///It is equivalent to `range(indirect_iterator(pB,pFunc),indirect_iterator(pE,pFunc))`
template<typename tSub>
inline CRange<ITransform<tSub,fn::ADereference>> indirect_range(const tSub &pB,const tSub &pE)
{
    return {indirect_iterator(pB),indirect_iterator(pE)};
}

///returns a range that can be used to access the objects pointed-to by another range

///\param pRange the range of pointers
///
///The range will contain the elements pointed to by the elements in the original range
///
///It is equivalent to `indirect_range(range_begin(pRange),range_end(pRange))`
template<typename tRange>
inline auto indirect_range(const tRange &pRange) -> decltype(indirect_range(range_begin(pRange),range_end(pRange)))
{
    return indirect_range(range_begin(pRange),range_end(pRange));
}

///returns an object that can be used to transform ranges

///The only safe way to use the returned object is to use operator| to filter a range, as in:
///
///~~~~~~~~
///std::vector<int*> lV
///
///...
///
///for(int i : lV | range_indirect())
///    msg_info() << i << "\n";
///~~~~~~~~
///
///which will print the numbers pointed to by the elements of `lV`
template<typename tFunc>
inline detail::CTransformForwarder<fn::ADereference> range_indirect()
{
    return {fn::ADereference()};
}

//@}

///@}

/*namespace buola*/ }

#endif
